package com.zbkj.front.service.fund.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.beust.jcommander.internal.Lists;
import com.github.pagehelper.PageHelper;
import com.zbkj.dal.base.constants.OrderConstants;
import com.zbkj.dal.base.enums.*;
import com.zbkj.dal.base.page.CommonPage;
import com.zbkj.dal.common.exception.CrmebException;
import com.zbkj.dal.common.utils.CrmebUtil;
import com.zbkj.dal.common.utils.MoneyUtil;
import com.zbkj.dal.dao.SystemUserLevelEquityDao;
import com.zbkj.dal.dao.UserBalanceRecordDao;
import com.zbkj.dal.dao.UserBankDao;
import com.zbkj.dal.dao.UserWithdrawRecordDao;
import com.zbkj.dal.entity.fund.UserFundAccount;
import com.zbkj.dal.entity.fund.UserQuotaChangeRecord;
import com.zbkj.dal.entity.fund.UserWithdrawRecord;
import com.zbkj.dal.entity.system.SystemUserLevel;
import com.zbkj.dal.entity.user.UserBalanceRecord;
import com.zbkj.dal.entity.user.UserBank;
import com.zbkj.dal.pojo.dto.MonthAmountItemDTO;
import com.zbkj.dal.pojo.request.PageParamRequest;
import com.zbkj.dal.token.FrontTokenComponent;
import com.zbkj.front.model.vo.LastRecordMonthVO;
import com.zbkj.front.service.fund.FrontFundV2Service;
import com.zbkj.service.manager.FundManager;
import com.zbkj.service.manager.QuotaManager;
import com.zbkj.service.model.dto.SaveUserBalanceRecordDTO;
import com.zbkj.service.model.front.req.fund.CrabRoeFundFlowReq;
import com.zbkj.service.model.front.req.fund.WithdrawApplyReq;
import com.zbkj.service.model.front.vo.fund.CrabRoeAccountVO;
import com.zbkj.service.model.front.vo.fund.MonthCrabRoeVO;
import com.zbkj.service.model.front.vo.fund.UserQuotaVO;
import com.zbkj.service.model.front.vo.fund.WithdrawDetailVO;
import com.zbkj.service.model.front.vo.user.*;
import com.zbkj.service.service.impl.FundV2BaseService;
import com.zbkj.service.util.DateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class FrontFundV2ServiceImpl extends FundV2BaseService implements FrontFundV2Service {

    @Resource
    private UserBankDao userBankDao;

    @Resource
    private FundManager fundManager;

    @Resource
    private QuotaManager quotaManager;

    @Resource
    private FrontTokenComponent frontTokenComponent;

    @Resource
    private UserBalanceRecordDao userBalanceRecordDao;

    @Resource
    private UserWithdrawRecordDao userWithdrawRecordDao;

    @Resource
    private SystemUserLevelEquityDao systemUserLevelEquityDao;

    @Resource
    private AliNewSmsServiceImpl aliNewSmsService;

    @Override
    public UserFundAccountVO userFundAccount() {
        Integer userId = frontTokenComponent.userId();
        // 查询用户额度信息
        UserQuotaVO userQuotaVO = quotaManager.userQuota(userId);
        // 查询用户雅识账户信息
        CrabRoeAccountVO crabRoeAccountVO = fundManager.userCrabRoeAccount(userId);
        return UserFundAccountVO.generate(userQuotaVO, crabRoeAccountVO);
    }

    @Override
    public CrabRoeAccountVO userCrabRoeAccount() {
        Integer userId = frontTokenComponent.userId();
        return fundManager.userCrabRoeAccount(userId);
    }

    @Override
    public UserQuotaInfoVO userUserQuotaInfo() {
        // 查询用户额度信息
        Integer userId = frontTokenComponent.userId();
        UserQuotaVO userQuotaVO = quotaManager.userQuota(userId);

        // 查询下一级信息
        SystemUserLevel systemUserLevel = quotaManager.nextLevelInfo(userQuotaVO.getLevelId());
        return UserQuotaInfoVO.generate(userQuotaVO, systemUserLevel);
    }


    @Override
    public List<CurrentFundFlowVO> currentFundFlow() {
        List<CurrentFundFlowVO> currentFundFlowList = new ArrayList<>();

        // 查询用户雅识账户信息
        Integer userId = frontTokenComponent.userId();
        UserFundAccount userFundAccount = fundManager.findByIdAndAccountType(userId, FundAccountType.CRAB_ROE.name());

        LocalDate now = LocalDate.now();
        for (int i = 0; i < 6; i++) {
            LocalDate date = now.minusMonths(i);
            Integer monthBegin = DateTimeUtil.monthBegin(date);
            Integer monthEnd = DateTimeUtil.monthEnd(date);
            LambdaQueryWrapper<UserBalanceRecord> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(UserBalanceRecord::getFundAccount, userFundAccount.getFundAccount());
            wrapper.ge(UserBalanceRecord::getBillDate, monthBegin);
            wrapper.le(UserBalanceRecord::getBillDate, monthEnd);
            wrapper.eq(UserBalanceRecord::getDeleted, BooleanEnums.FALSE.getCode());
            wrapper.orderByDesc(UserBalanceRecord::getId);
            List<UserBalanceRecord> userBalanceRecords = userBalanceRecordDao.selectList(wrapper);
            if (CollectionUtil.isNotEmpty(userBalanceRecords)) {
                currentFundFlowList.add(CurrentFundFlowVO.generate(date, userBalanceRecords));
            }
        }
        return currentFundFlowList;
    }

    @Override
    public CommonPage<FundFlowVO> crabRoeFundFlow(CrabRoeFundFlowReq req) {
        // 查询用户雅识账户信息
        Integer userId = frontTokenComponent.userId();
        UserFundAccount userFundAccount = fundManager.findByIdAndAccountType(userId, FundAccountType.CRAB_ROE.name());

        LambdaQueryWrapper<UserBalanceRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserBalanceRecord::getFundAccount, userFundAccount.getFundAccount());
        wrapper.ge(UserBalanceRecord::getBillDate, req.monthBegin());
        wrapper.le(UserBalanceRecord::getBillDate, req.monthEnd());
        wrapper.eq(UserBalanceRecord::getDeleted, BooleanEnums.FALSE.getCode());
        wrapper.eq(Objects.nonNull(req.getTradeDirection()), UserBalanceRecord::getType, req.getTradeDirection());
        wrapper.eq(Objects.nonNull(req.getTradeType()), UserBalanceRecord::getLinkType, req.getTradeType());
        wrapper.eq(Objects.equals(req.getFrozen(), BooleanEnums.TRUE.getCode()), UserBalanceRecord::getStatus, FundStatusEnums.IN_TRANSIT.getCode());
        wrapper.orderByDesc(UserBalanceRecord::getId);
        PageHelper.startPage(req.getPage(), req.getLimit());
        List<UserBalanceRecord> userBalanceRecords = userBalanceRecordDao.selectList(wrapper);
        CommonPage<UserBalanceRecord> userBalanceRecordPage = CommonPage.restPage(userBalanceRecords);

        CommonPage<FundFlowVO> fundFlowVOCommonPage = new CommonPage<>();
        fundFlowVOCommonPage.setPage(userBalanceRecordPage.getPage());
        fundFlowVOCommonPage.setLimit(userBalanceRecordPage.getLimit());
        fundFlowVOCommonPage.setTotalPage(userBalanceRecordPage.getTotalPage());
        fundFlowVOCommonPage.setTotal(userBalanceRecordPage.getTotal());
        fundFlowVOCommonPage.setList(userBalanceRecords.stream().map(FundFlowVO::transform).collect(Collectors.toList()));
        return fundFlowVOCommonPage;
    }

    @Override
    public MonthCrabRoeVO monthCrabRoeFlow(String month) {
        Integer userId = frontTokenComponent.userId();

        Integer begin = Integer.parseInt(month.replaceAll("-", "") + "01");
        LocalDateTime time = LocalDateTimeUtil.parse(month, DatePattern.NORM_MONTH_FORMATTER);
        LocalDate localDate = time.toLocalDate().plusMonths(1);
        Integer end = Integer.valueOf(LocalDateTimeUtil.format(localDate, DatePattern.PURE_DATE_PATTERN));

        List<MonthAmountItemDTO> monthAmountItems = userBalanceRecordDao.monthAmountItem(userId, begin, end);

        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal receivedAmount = BigDecimal.ZERO;
        BigDecimal frozenAmount = BigDecimal.ZERO;
        for (MonthAmountItemDTO monthAmountItem : monthAmountItems) {
            if (StringUtils.equals(monthAmountItem.getStatus(), FundStatusEnums.IN_TRANSIT.getCode())) {
                if (Objects.equals(monthAmountItem.getType(), FundDirectionEnums.INCOME.getCode())) {
                    totalAmount = totalAmount.add(monthAmountItem.getAmount());
                    frozenAmount = frozenAmount.add(monthAmountItem.getAmount());
                } else {
                    totalAmount = totalAmount.subtract(monthAmountItem.getAmount());
                    frozenAmount = frozenAmount.subtract(monthAmountItem.getAmount());
                }
            } else if (StringUtils.equals(monthAmountItem.getStatus(), FundStatusEnums.FINISH.getCode())) {
                if (Objects.equals(monthAmountItem.getType(), FundDirectionEnums.INCOME.getCode())) {
                    totalAmount = totalAmount.add(monthAmountItem.getAmount());
                    receivedAmount = receivedAmount.add(monthAmountItem.getAmount());
                } else {
                    totalAmount = totalAmount.subtract(monthAmountItem.getAmount());
                    receivedAmount = receivedAmount.subtract(monthAmountItem.getAmount());
                }
            }
        }

        MonthCrabRoeVO monthCrabRoeVO = new MonthCrabRoeVO();
        monthCrabRoeVO.setIncomeAmount(MoneyUtil.prettyFormat(totalAmount));
        monthCrabRoeVO.setReceivedAmount(MoneyUtil.prettyFormat(receivedAmount));
        monthCrabRoeVO.setFrozenAmount(MoneyUtil.prettyFormat(frozenAmount));
        return monthCrabRoeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdrawApply(WithdrawApplyReq req) {
        if (req.getWithdrawAmount().compareTo(BigDecimal.valueOf(100)) < 0) {
            throw new CrmebException("单次提现金额不能小于100元");
        }
        if (req.getWithdrawAmount().compareTo(BigDecimal.valueOf(5000)) > 0) {
            throw new CrmebException("单次提现金额不能大于5000元");
        }

        // 查询银行卡信息
        Integer userId = frontTokenComponent.userId();

        LambdaQueryWrapper<UserWithdrawRecord> withdrawWrapper = new LambdaQueryWrapper<>();
        withdrawWrapper.eq(UserWithdrawRecord::getUserId, userId);
        withdrawWrapper.in(UserWithdrawRecord::getStatus, Lists.newArrayList(WithdrawStatusEnums.WAIT_AUDIT.getCode(), WithdrawStatusEnums.AUDIT_PASS.getCode(), WithdrawStatusEnums.SUCCESS.getCode()));
        withdrawWrapper.ge(UserWithdrawRecord::getCreateTime, LocalDate.now().atStartOfDay());
        withdrawWrapper.le(UserWithdrawRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MAX));
        List<UserWithdrawRecord> userWithdrawRecords = userWithdrawRecordDao.selectList(withdrawWrapper);
        BigDecimal alreadyWithdrawAmount = userWithdrawRecords.stream().map(UserWithdrawRecord::getWithdrawAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (alreadyWithdrawAmount.add(req.getWithdrawAmount()).compareTo(BigDecimal.valueOf(5000)) > 0) {
            throw new CrmebException("单日提现金额不能大于5000元");
        }

        LambdaQueryWrapper<UserBank> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserBank::getBankcardNo, req.getBankcardNo());
        wrapper.eq(UserBank::getUserId, userId);
        wrapper.eq(UserBank::getDeleted, BooleanEnums.FALSE.getCode());
        UserBank userBank = userBankDao.selectOne(wrapper);
        if (Objects.isNull(userBank)) {
            throw new CrmebException("银行卡信息异常，请确认后重试");
        }

        // 扣减雅识
        UserFundAccount userFundAccount = fundManager.minusAvailableAmount(userId, req.getWithdrawAmount(), FundAccountType.CRAB_ROE.name());

        // 创建提现记录
        UserWithdrawRecord userWithdrawRecord = new UserWithdrawRecord();
        userWithdrawRecord.setUserId(userId);
        userWithdrawRecord.setFundAccountId(userFundAccount.getId());
        userWithdrawRecord.setFundAccount(userFundAccount.getFundAccount());
        userWithdrawRecord.setWithdrawNo(CrmebUtil.getOrderNo(OrderConstants.USER_WITHDRAW_ORDER_PREFIX));
        userWithdrawRecord.setBankcardNumber(userBank.getBankcardNumber());
        userWithdrawRecord.setBankAccountName(userBank.getAccountName());
        userWithdrawRecord.setBankCode(userBank.getBankNumber());
        userWithdrawRecord.setBankName(userBank.getBankName());
        userWithdrawRecord.setWithdrawAmount(req.getWithdrawAmount());
        userWithdrawRecord.setBillDate(DateTimeUtil.todayInt());
        userWithdrawRecord.setStatus(WithdrawStatusEnums.WAIT_AUDIT.getCode());
        userWithdrawRecord.setRemark(req.getRemark());
        userWithdrawRecord.setCreateTime(LocalDateTime.now());
        userWithdrawRecord.setUpdateTime(LocalDateTime.now());
        userWithdrawRecord.setDeleted(BooleanEnums.FALSE.getCode());
        userWithdrawRecordDao.insert(userWithdrawRecord);

        SaveUserBalanceRecordDTO dto = SaveUserBalanceRecordDTO.builder().userFundAccount(userFundAccount)
                .status(FundStatusEnums.FINISH.getCode()).bizOrderNo(userWithdrawRecord.getWithdrawNo())
                .tradeAmount(req.getWithdrawAmount()).fundType(FundTradeTypeEnums.WITHDRAW.getCode())
                .fundDirection(FundDirectionEnums.OUTCOME.getCode()).remark(String.format("%s %s", userWithdrawRecord.getBankName(), userWithdrawRecord.getBankcardNumber().substring(userWithdrawRecord.getBankcardNumber().length() - 4)))
                .image(FundTradeTypeEnums.WITHDRAW.getDefaultImg()).build();
        fundManager.saveUserBalanceRecord(dto);

        //发送短信
        aliNewSmsService.sendFundMsg("提现申请");

        return userWithdrawRecord.getWithdrawNo();
    }

    @Override
    public CommonPage<QuotaFundFlowVO> totalQuotaFundFlow(PageParamRequest req) {
        Integer userId = frontTokenComponent.userId();
        LambdaQueryWrapper<UserQuotaChangeRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Objects.nonNull(userId), UserQuotaChangeRecord::getUserId, userId);
        wrapper.in(UserQuotaChangeRecord::getChangeType, QuotaChangeType.levelCalculateList());
        wrapper.eq(UserQuotaChangeRecord::getDeleted, BooleanEnums.FALSE.getCode());
        wrapper.orderByDesc(UserQuotaChangeRecord::getId);
        return super.quotaFundFlow(wrapper, req);
    }

    @Override
    public CommonPage<QuotaFundFlowVO> availableQuotaFundFlow(PageParamRequest req) {
        Integer userId = frontTokenComponent.userId();
        LambdaQueryWrapper<UserQuotaChangeRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Objects.nonNull(userId), UserQuotaChangeRecord::getUserId, userId);
        wrapper.eq(UserQuotaChangeRecord::getDeleted, BooleanEnums.FALSE.getCode());
        wrapper.orderByDesc(UserQuotaChangeRecord::getId);
        return super.quotaFundFlow(wrapper, req);
    }

    @Override
    public LastRecordMonthVO lastRecordMonth() {
        Integer userId = frontTokenComponent.userId();
        LambdaQueryWrapper<UserBalanceRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserBalanceRecord::getDeleted, BooleanEnums.FALSE.getCode());
        wrapper.eq(UserBalanceRecord::getUid, userId);
        wrapper.orderByDesc(UserBalanceRecord::getId);
        wrapper.last("limit 1");
        UserBalanceRecord userBalanceRecord = userBalanceRecordDao.selectOne(wrapper);
        if (Objects.isNull(userBalanceRecord)) {
            return LastRecordMonthVO.generateFalse();
        }
        if (Objects.isNull(userBalanceRecord.getBillDate()) || userBalanceRecord.getBillDate() <= 0) {
            return LastRecordMonthVO.generateFalse();
        }
        try {
            LocalDateTime time = LocalDateTimeUtil.parse(String.valueOf(userBalanceRecord.getBillDate()), DatePattern.PURE_DATE_PATTERN);
            return LastRecordMonthVO.generate(LocalDateTimeUtil.format(time, DatePattern.NORM_MONTH_PATTERN));
        } catch (Exception e) {
            return LastRecordMonthVO.generateFalse();
        }
    }

    @Override
    public WithdrawDetailVO withdrawDetail(String withdrawNo) {
        LambdaQueryWrapper<UserWithdrawRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserWithdrawRecord::getWithdrawNo, withdrawNo);
        wrapper.eq(UserWithdrawRecord::getDeleted, BooleanEnums.FALSE.getCode());
        UserWithdrawRecord userWithdrawRecord = userWithdrawRecordDao.selectOne(wrapper);
        if (Objects.isNull(userWithdrawRecord)) {
            throw new CrmebException("未查询到对应的提现记录");
        }
        return WithdrawDetailVO.getWithdrawDetailVO(userWithdrawRecord);
    }

}
